import { AZExcel, ITable } from '../types';
import "reflect-metadata";
import { ZTable } from '../cors/table';
import { Hook } from '../cors/hook';
function getInstanceByConstructor(arr: any[], construct: any) {
    for(let item of arr) {
        if(item instanceof construct) {
            return item;
        }
    }
    return null;
}
export function tableFactory<T = ITable>(table: {new(...args: any):T}, excelInstancel: AZExcel, preConstruct: {new(...args: any):any}[] = []): T {
    let instance: any[] = [];
    
    // table.prototype.instance = instance;
    function createInstance(target: {new(...args: any):any}) {
        // @ts-ignore
        if(target.isMain) {
            target.prototype.getAllIntance = () => {
                return instance;
            }
        }
        for(let item of instance) {
            if(item.__proto__.constructor == target) {
                return item;
            }
        }
        let params: any [] = [];
        let types = (<any>Reflect).getMetadata("design:paramtypes", target);
        if(target.prototype['__parameters__'] == undefined) {
            target.prototype['__parameters__'] = [];
        }
        if(types instanceof Array && types.length > 0) {
            for(let i = 0; i < types.length; i++) {
                if(Object.prototype.toString.call(types[i]) == '[object Function]' && target.prototype['__parameters__'][i] == undefined) {
                    params.push(createInstance(types[i]));
                } else {
                    if(target.prototype['__parameters__'] instanceof Array) {
                        try {
                            // console.log(target)
                            let token = target.prototype['__parameters__'][i].token;
                            // @ts-ignore
                            params.push(excelInstancel.inject.get(token));                            
                        } catch (error) {
                            console.log('````````````````````````````````````````````1')
                            console.log(Object.prototype.toString.call(types[i]), types)
                            console.log(target.prototype['__parameters__']);
                            console.dir(target);
                            console.log(instance)
                            throw error;
                        }
                    } else {
                        params.push(null);
                    }
                }
            }
        }
        let t: any;
        t = new target(...params);
        instance.push(t);
        
        function factory(cust: any, isSingle = false) {
            if(isSingle) {
                for(let item of instance) {
                    if(item instanceof cust) {
                        return item;
                    }
                }
            }
            let factParams: any[] = [];
            let types = (<any>Reflect).getMetadata("design:paramtypes", cust);
            if(cust.prototype['__parameters__'] == undefined) {
                cust.prototype['__parameters__'] = [];
            }
            if(types instanceof Array && types.length > 0) {
                for(let i = 0; i < types.length; i++) {
                    if(Object.prototype.toString.call(types[i]) == '[object Function]' && cust.prototype['__parameters__'][i] == undefined) {
                        if(types[i] === undefined) {
                            console.log(cust, types)
                        }
                        try {
                            factParams.push(createInstance(types[i]));
                        } catch (error) {
                            console.log(cust, types)
                        }
                        
                        
                    } else {
                        if(cust.prototype['__parameters__'] instanceof Array) {
                            try {
                                let token = cust.prototype['__parameters__'][i].token;
                                // @ts-ignore
                                // factParams.push(excelInstancel.inject.get(token));
                                // @ts-ignore
                                if(excelInstancel.inject.get(token))   {
                                    // @ts-ignore
                                    factParams.push(excelInstancel.inject.get(token));
                                } else if(token === 'factory') {
                                    factParams.push(factory);
                                } else {
                                    // console.log(token)
                                    // @ts-ignore
                                    for(let temp of instance) {
                                        if(temp.constructor.injectName === token) {
                                            // @ts-ignore
                                            factParams.push(temp);
                                            break;
                                        }
                                    }
                                }                           
                            } catch (error) {
                                throw error;
                            }
                        } else {
                            factParams.push(null);
                        }
                    }
                }
            } else {
                let tokens = cust.prototype['__parameters__'];
                if(tokens instanceof Array) {
                    for(let i = 0; i < tokens.length; i++) {
                        let token = tokens[i].token;
                        // @ts-ignore
                        if(excelInstancel.inject.get(token))   {
                            // @ts-ignore
                            factParams.push(excelInstancel.inject.get(token));
                        } else {
                            // console.log(token)
                            // @ts-ignore
                            for(let temp of instance) {
                                if(temp.constructor.injectName === token) {
                                    // @ts-ignore
                                    factParams.push(temp);
                                    break;
                                }
                            }
                        }  
                    }
                }
            }
            let d = new cust(...factParams);
            d.factory =  factory;
            if(d.init) {
                d.init();
            }
            if(isSingle) {
                instance.push(d);
            }
            return d;
        }
        try {
            t.factory = factory;
            t.getInstance = function(func: {new(): any}) {
                for(let item of this.instance) {
                    if(item instanceof func) {
                        return item;
                    }
                }
            }
        } catch (error) {
            
        }
        
        return t;
    }
    // for(let item of preConstruct) {
    //     let isExist = false;
    //     for(let inst of instance) {
    //         if(inst.prototype.constructor === item) {
    //             isExist = true;
    //         }
    //     }
    //     if(isExist === false) {
    //         createInstance(item);
    //     }
    // }
    let values = createInstance(table);
    // for(let item of instance) {
    //     // console.log(item)
    //     Object.defineProperty(item, '__getHiddenDependents', {
    //         value: (construct: any) => {
    //             for(let cnt of instance) {
    //                 if(cnt.prototype.constructor === construct) {
    //                     return cnt;
    //                 }
    //             }
    //             return null;
    //         }
    //     })
    // }
    Object.defineProperty(values, 'injects', {
        enumerable: false,
        value: instance
    });
    function triggerRender() {
        values.render();
    }
    for(let item of instance) {
        if(item.init) {
            item.init();
            if(item.constructor.prototype.__ADD_HOOK__) {
                let depInstance = getInstanceByConstructor(instance, Hook) as Hook;
                /**
                 *  hookName: name,
                    hookFuncName: key 
                 */
                for(let hookinfo of item.constructor.prototype.__ADD_HOOK__) {
                    depInstance[(hookinfo.hookName as Exclude<keyof Hook, 'createCustomHook'|'removeCustomHook'>)].add(item[hookinfo.hookFuncName].bind(item))
                }
                // let depInstance = item.__getHiddenDependents(Hook);
            }
            if((instance instanceof ZTable) == false) {
                let temp = item.triggerRender;
                if(temp == undefined) {
                    item.triggerRender = function() {
                        values.render();
                    };
                } else {
                    item.triggerRender = function() {
                        if(temp() != false) {
                            triggerRender();
                        }
                    };
                }
            }
        }
    }
    // console.log(instance);
    if((<any>window)['zTable'] == undefined) {
        (<any>window)['zTable'] = {
            table: []
        }
    }
    (<any>window)['zTable']['table'] = instance;
    if(__ENV__ === 'development') {
        // @ts-ignore
        window.zTableInstance = instance
    }
    // values.instance =  instance;
    return values;
}
